use crate::{
    ast::{types, Expression, Type},
    error::{CompilerError, Result},
    llvm_backend::LLVMBackend,
};
use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace};

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate append call for slices
    pub(in crate::llvm_backend) fn generate_append_call(
        &mut self,
        arguments: &[Expression],
    ) -> Result<BasicValueEnum<'ctx>> {
        // First argument is the slice, remaining arguments are elements to append
        let slice_arg = &arguments[0];
        let new_elements = &arguments[1..];

        // Get the type of the slice
        let vector_type = if let Expression::Variable(var_name) = slice_arg {
            self.variable_type_map.get(var_name).cloned()
        } else {
            None
        };

        let element_type = if let Some(Type::Vec(slice_t)) = &vector_type {
            *slice_t.element_type.clone()
        } else {
            return Err(CompilerError::internal(
                "append() requires a slice as first argument",
            ));
        };

        // Generate the slice value
        let slice_value = self.generate_expression(slice_arg)?;

        if !slice_value.is_struct_value() {
            return Err(CompilerError::internal("Slice value is not a struct"));
        }

        let slice_struct = slice_value.into_struct_value();

        // Extract fields from the slice struct
        let old_ptr = self
            .builder
            .build_extract_value(slice_struct, 0, "old_ptr")?
            .into_pointer_value();
        let old_len = self
            .builder
            .build_extract_value(slice_struct, 1, "old_len")?
            .into_int_value();
        let old_cap = self
            .builder
            .build_extract_value(slice_struct, 2, "old_cap")?
            .into_int_value();

        // Calculate new length
        let num_new_elements = new_elements.len() as u64;
        let new_len = self.builder.build_int_add(
            old_len,
            self.context.i64_type().const_int(num_new_elements, false),
            "new_len",
        )?;

        // Check if we need to reallocate
        let need_realloc = self.builder.build_int_compare(
            inkwell::IntPredicate::UGT,
            new_len,
            old_cap,
            "need_realloc",
        )?;

        // Get element type and size.
        // For non-basic types we default to pointer size (8 bytes) except
        // for Rc/Weak trait handles, which are stored as {ptr, ptr}.
        let element_llvm_type = self.nrc_type_to_llvm_type(&Some(element_type.clone()))?;
        let element_size = match &element_type {
            Type::Basic(basic_type) => match basic_type {
                types::BasicType::Int8 | types::BasicType::Uint8 | types::BasicType::Char => 1,
                types::BasicType::Int16 | types::BasicType::Uint16 => 2,
                types::BasicType::Int32 | types::BasicType::Uint32 | types::BasicType::Float32 => 4,
                types::BasicType::Int
                | types::BasicType::Int64
                | types::BasicType::Uint
                | types::BasicType::Uint64
                | types::BasicType::Float64 => 8,
                types::BasicType::Bool => 1,
                _ => 8,
            },
            Type::Rc(rc_type) => {
                if self.trait_name_from_type(&rc_type.inner_type).is_some() {
                    // Rc<Trait> handle: {ptr, ptr}
                    16
                } else {
                    8
                }
            }
            Type::Weak(weak_type) => {
                if self.trait_name_from_type(&weak_type.inner_type).is_some() {
                    // Weak<Trait> handle: {ptr, ptr}
                    16
                } else {
                    8
                }
            }
            _ => 8,
        };

        // Create basic blocks for the if-else
        let current_func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("No current function"))?;
        let then_block = self
            .context
            .append_basic_block(current_func, "realloc_block");
        let else_block = self
            .context
            .append_basic_block(current_func, "no_realloc_block");
        let merge_block = self.context.append_basic_block(current_func, "merge_block");

        // Build conditional branch
        self.builder
            .build_conditional_branch(need_realloc, then_block, else_block)?;

        // Then block: reallocate
        self.builder.position_at_end(then_block);
        let new_cap_then = self.builder.build_int_mul(
            old_cap,
            self.context.i64_type().const_int(2, false),
            "new_cap_doubled",
        )?;
        // Make sure new_cap is at least new_len
        let new_cap_then = self
            .builder
            .build_select(
                self.builder.build_int_compare(
                    inkwell::IntPredicate::ULT,
                    new_cap_then,
                    new_len,
                    "cap_check",
                )?,
                new_len,
                new_cap_then,
                "new_cap_then",
            )?
            .into_int_value();

        let new_size_bytes = self.builder.build_int_mul(
            new_cap_then,
            self.context.i64_type().const_int(element_size, false),
            "new_size_bytes",
        )?;

        let malloc_func = self.function_map.get("__malloc").unwrap();
        let malloc_call =
            self.builder
                .build_call(*malloc_func, &[new_size_bytes.into()], "malloc_call")?;
        let new_ptr_i8_then = malloc_call
            .try_as_basic_value()
            .left()
            .unwrap()
            .into_pointer_value();
        let new_ptr_then = self
            .builder
            .build_bitcast(
                new_ptr_i8_then,
                element_llvm_type.ptr_type(AddressSpace::default()),
                "new_ptr_then",
            )?
            .into_pointer_value();

        // Copy old elements to new array using memcpy
        // Calculate the number of bytes to copy: old_len * element_size
        let element_size_const = self.context.i64_type().const_int(element_size, false);
        let copy_size = self
            .builder
            .build_int_mul(old_len, element_size_const, "copy_size")?;

        // Get memcpy intrinsic
        let memcpy_intrinsic = self
            .module
            .get_function("llvm.memcpy.p0.p0.i64")
            .unwrap_or_else(|| {
                let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
                let memcpy_type = self.context.void_type().fn_type(
                    &[
                        i8_ptr_type.into(),              // dest
                        i8_ptr_type.into(),              // src
                        self.context.i64_type().into(),  // len
                        self.context.bool_type().into(), // is_volatile
                    ],
                    false,
                );
                self.module
                    .add_function("llvm.memcpy.p0.p0.i64", memcpy_type, None)
            });

        // Cast pointers to i8* for memcpy
        let dest_i8 = self
            .builder
            .build_bitcast(
                new_ptr_then,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "dest_i8",
            )?
            .into_pointer_value();

        let src_i8 = self
            .builder
            .build_bitcast(
                old_ptr,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "src_i8",
            )?
            .into_pointer_value();

        // Call memcpy
        self.builder.build_call(
            memcpy_intrinsic,
            &[
                dest_i8.into(),
                src_i8.into(),
                copy_size.into(),
                self.context.bool_type().const_int(0, false).into(), // is_volatile = false
            ],
            "memcpy_call",
        )?;

        // Free old memory
        let free_func = self
            .function_map
            .get("free")
            .ok_or_else(|| CompilerError::internal("free function not found"))?;
        self.builder
            .build_call(*free_func, &[src_i8.into()], "free_old_memory")?;

        self.builder.build_unconditional_branch(merge_block)?;

        // Else block: use existing array
        self.builder.position_at_end(else_block);
        let new_ptr_else = old_ptr;
        let new_cap_else = old_cap;
        self.builder.build_unconditional_branch(merge_block)?;

        // Merge block
        self.builder.position_at_end(merge_block);
        let new_ptr_phi = self.builder.build_phi(
            element_llvm_type.ptr_type(AddressSpace::default()),
            "new_ptr",
        )?;
        new_ptr_phi.add_incoming(&[(&new_ptr_then, then_block), (&new_ptr_else, else_block)]);
        let new_ptr = new_ptr_phi.as_basic_value().into_pointer_value();

        let new_cap_phi = self.builder.build_phi(self.context.i64_type(), "new_cap")?;
        new_cap_phi.add_incoming(&[(&new_cap_then, then_block), (&new_cap_else, else_block)]);
        let new_cap = new_cap_phi.as_basic_value().into_int_value();

        // Append new elements
        for (i, new_elem_expr) in new_elements.iter().enumerate() {
            let mut new_elem_value = self.generate_expression(new_elem_expr)?;

            // Type conversion if needed
            if new_elem_value.get_type() != element_llvm_type {
                if new_elem_value.is_float_value() && element_llvm_type.is_float_type() {
                    new_elem_value = self
                        .builder
                        .build_float_cast(
                            new_elem_value.into_float_value(),
                            element_llvm_type.into_float_type(),
                            "float_cast",
                        )?
                        .into();
                } else if new_elem_value.is_int_value() && element_llvm_type.is_int_type() {
                    let int_val = new_elem_value.into_int_value();
                    let target_int_type = element_llvm_type.into_int_type();
                    if int_val.get_type().get_bit_width() < target_int_type.get_bit_width() {
                        new_elem_value = self
                            .builder
                            .build_int_z_extend(int_val, target_int_type, "int_ext")?
                            .into();
                    } else if int_val.get_type().get_bit_width() > target_int_type.get_bit_width() {
                        new_elem_value = self
                            .builder
                            .build_int_truncate(int_val, target_int_type, "int_trunc")?
                            .into();
                    }
                }
            }

            let append_idx = self.builder.build_int_add(
                old_len,
                self.context.i64_type().const_int(i as u64, false),
                "append_idx",
            )?;
            let append_ptr = unsafe {
                self.builder
                    .build_gep(element_llvm_type, new_ptr, &[append_idx], "append_ptr")?
            };
            self.builder.build_store(append_ptr, new_elem_value)?;
        }

        // Create new slice struct
        let ptr_type = element_llvm_type.ptr_type(AddressSpace::default());
        let slice_struct_type = self.context.struct_type(
            &[
                ptr_type.into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        let slice_alloca = self
            .builder
            .build_alloca(slice_struct_type, "result_slice")?;

        // Store ptr field
        let ptr_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(0, false),
                ],
                "ptr_field",
            )?
        };
        self.builder.build_store(ptr_field_ptr, new_ptr)?;

        // Store len field
        let len_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(1, false),
                ],
                "len_field",
            )?
        };
        self.builder.build_store(len_field_ptr, new_len)?;

        // Store cap field
        let cap_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(2, false),
                ],
                "cap_field",
            )?
        };
        self.builder.build_store(cap_field_ptr, new_cap)?;

        // Load and return the new slice
        let result_slice =
            self.builder
                .build_load(slice_struct_type, slice_alloca, "result_slice")?;
        Ok(result_slice)
    }
}
